package rubik;

import aima.core.agent.Action;
import aima.core.search.framework.Problem;
import aima.core.search.framework.Search;
import aima.core.search.framework.SearchAgent;
import aima.core.search.framework.TreeSearch;
import aima.core.search.uninformed.*;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import javax.swing.JOptionPane;

//   U
// L F R B
//   D
public class RubikCubeDemo {
    private static Cube scrambled;
    public static void main(String[] args) {
        int caselle;
        do {
        caselle = Integer.parseInt(JOptionPane.showInputDialog(null, "Dimensione del cubo? (min 2, max 10)"));
        } while (caselle >10 || caselle <2);
        Cube cube = new Cube(caselle,true);
        int t = Integer.parseInt(JOptionPane.showInputDialog(null, "Quante mosse di scrambing? (zero è random)"));
        if (t == 0) t = (int)(Math.random()*11) + 20;
        Scrambler.Scramble(t,cube);
        scrambled = new Cube(cube);
        boolean ok;
        do {
            String algorithm = JOptionPane.showInputDialog(null, "Metodo? (BFS/DFS/DLS/IDS/UCS)");
            ok = false;            
            if (algorithm.equals("bfs")) {
                RubikCubeBreadthFirstSearch(cube);
                ok = true;
            }
            if (algorithm.equals("dfs")) {
                RubikCubeDepthFirstSearch(cube);
                ok = true;
            }
            if (algorithm.equals("dls")) {
                RubikCubeDephtLimitedSearch(cube,t);
                ok = true;
            }
            if (algorithm.equals("ids")) {
                RubikCubeIterativeDeepeningSearch(cube);
                ok = true;
            }
            if (algorithm.equals("ucs")) {
                RubikCubeUniformCostSearch(cube);
                ok = true;
            }
            /*if (algorithm.equals("bds")) {
                RubikCubeBidirectionalSearch(cubo);
                ok = true;
            }*/
        } while (!ok);
    }

    private static void RubikCubeBreadthFirstSearch(Cube cube) {
        try {
            long time = System.nanoTime();
            Problem problem = new Problem(cube,
                    RubikCubeFunctionFactory.getActionsFunction(),
                    RubikCubeFunctionFactory.getResultFunction(),
                    new RubikCubeGoalTest());
            Search search = new BreadthFirstSearch(new TreeSearch());
            SearchAgent agent = new SearchAgent(problem, search);
            printAll("RubikCubeDemo BFS", agent, time);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    
    private static void RubikCubeDepthFirstSearch(Cube cube) {
        try {
            long time = System.nanoTime();
            Problem problem = new Problem(cube,
                    RubikCubeFunctionFactory.getActionsFunction(),
                    RubikCubeFunctionFactory.getResultFunction(),
                    new RubikCubeGoalTest());
            Search search = new DepthFirstSearch(new TreeSearch());
            SearchAgent agent = new SearchAgent(problem, search);
            printAll("RubikCubeDemo DFS", agent, time);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    
    private static void RubikCubeDephtLimitedSearch(Cube cube, int n) {
        try {
            long time = System.nanoTime();
            if (n>20) n=20;
            Problem problem = new Problem(cube,
                    RubikCubeFunctionFactory.getActionsFunction(),
                    RubikCubeFunctionFactory.getResultFunction(),
                    new RubikCubeGoalTest());
            Search search = new DepthLimitedSearch(n);
            SearchAgent agent = new SearchAgent(problem, search);
            printAll("RubikCubeDemo DLS ("+n+")", agent, time);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    
    private static void RubikCubeIterativeDeepeningSearch(Cube cube) {
        try {
            long time = System.nanoTime();
            Problem problem = new Problem(cube,
                    RubikCubeFunctionFactory.getActionsFunction(),
                    RubikCubeFunctionFactory.getResultFunction(),
                    new RubikCubeGoalTest());
            Search search = new IterativeDeepeningSearch();
            SearchAgent agent = new SearchAgent(problem, search);
            printAll("RubikCubeDemo IDS", agent, time);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    
    private static void RubikCubeUniformCostSearch(Cube cube) {
        try {
            long time = System.nanoTime();
            Problem problem = new Problem(cube,
                    RubikCubeFunctionFactory.getActionsFunction(),
                    RubikCubeFunctionFactory.getResultFunction(),
                    new RubikCubeGoalTest());
            Search search = new UniformCostSearch(new TreeSearch());
            SearchAgent agent = new SearchAgent(problem, search);
            printAll("RubikCubeDemo UCS", agent, time);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
    
    /*private static void RubikCubeBidirectionalSearch(Cube cubo) {
        try {
            System.out.println("RubikCubeDemo BDS --> ");
            Problem problem = new Problem(cubo,
                    RubikCubeFunctionFactory.getActionsFunction(),
                    RubikCubeFunctionFactory.getResultFunction(),
                    new RubikCubeGoalTest());
            Search search = new BidirectionalSearch();
            SearchAgent agent = new SearchAgent(problem, search);
            printActions(agent.getActions());
            printInstrumentation(agent.getInstrumentation());
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }*/

    private static StringBuilder printActions(List<Action> actions) {
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < actions.size(); i++) {
            String action = actions.get(i).toString();
            buffer.append(action);
            buffer.append("\n");
        }
        return buffer;
    }

    private static StringBuilder printInstrumentation(Properties properties) {
        Iterator<Object> keys = properties.keySet().iterator();
        StringBuilder buffer = new StringBuilder();
        while (keys.hasNext()) {
            String key = (String) keys.next();
            String property = properties.getProperty(key);
            buffer.append(key).append(" : ").append(property).append("\n");
        }
        return buffer;
    }
    
    private static void printAll(String title, SearchAgent agent, long time){
        StringBuilder actions = printActions(agent.getActions());
        StringBuilder instruments = printInstrumentation(agent.getInstrumentation());
        instruments.append("Tempo d'esecuzione: ").append((System.nanoTime()-time)/Math.pow(10,6)).append(" ms");
        Solver solver = new Solver(scrambled,actions.toString());
        JOptionPane.showMessageDialog(null, actions.append(instruments), title, JOptionPane.PLAIN_MESSAGE);
        solver.run();
        JOptionPane.showMessageDialog(null, "RISOLTO!!!", "Risultato", JOptionPane.PLAIN_MESSAGE);
        System.exit(0);
    }
}